package com.beetle123;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.beetle123.model.*;
import com.beetle123.utils.DubboUtils;
import com.beetle123.utils.LogUtils;
import com.beetle123.utils.MyUtils;
import com.beetle123.utils.ParamParser;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import org.apache.commons.lang.StringUtils;


import java.net.URL;
import java.util.*;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * @author beetle
 */
public class MainController implements Initializable {

    @FXML
    public TextField tfThreadNum;
    @FXML
    public TextField tfTotalNum;
    @FXML
    private ComboBox<String> cbEnv;

    @FXML
    private ComboBox<String> cbProject;

    @FXML
    private ComboBox<String> cbService;

    @FXML
    private ComboBox<String> cbIpPort;

    @FXML
    private ComboBox<String> cbMethod;

    @FXML
    private ComboBox<String> cbParam;

    @FXML
    private Button btnSend;
    @FXML
    private Button btnGetService;

    @FXML
    private TextArea taRequest;
    @FXML
    private TextArea taResponse;


    private List<ConfigModel> configList;
    private Map<String, Map<String, ProjectModel>> envProjectMap = new HashMap<>();
    private Map<String, String> paramMap = new HashMap<>(16);

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        try {
            configList = JSON.parseArray(MyUtils.getConfig(), ConfigModel.class);
            initUI();
        } catch (Exception e) {
            LogUtils.println("初始化失败:\n" + MyUtils.getStackTrace(e));
            taResponse.setText("初始化失败\n" + MyUtils.getStackTrace(e));
            //taResponse.setText("初始化失败\n" + MyUtils.getStackTrace(e));
        }
    }

    @FXML
    private void btnSendAction(ActionEvent event) {
        new Thread(() -> invokeService()).start();
    }

    private int getInt(TextField textField, String name) {
        String text = textField.getText();
        if (StringUtils.isBlank(text)) {
            return 1;
        }
        try {
            int num = Integer.parseInt(text);
            if (num <= 0) {
                num = 1;
            }
            return num;
        } catch (Exception e) {
            MyUtils.alert(name + "非数字，请重新输入");
            return -1;
        }
    }

    private void invokeService() {
        AtomicReference<String> resultRef = new AtomicReference<>();
        try {
            Platform.runLater(() -> {
                taResponse.clear();
                btnSend.setText("请求中...");
                btnGetService.setDisable(true);
                btnSend.setDisable(true);
            });
            String envName = cbEnv.getSelectionModel().getSelectedItem();
            String serviceUniqueKey = cbService.getSelectionModel().getSelectedItem();
            String appName = cbProject.getSelectionModel().getSelectedItem();
            String methodName = cbMethod.getSelectionModel().getSelectedItem();
            String ipPort = cbIpPort.getSelectionModel().getSelectedItem();
            Map<String, ProjectModel> projectMap = envProjectMap.get(envName);
            ProjectModel project = projectMap.get(appName);
            ServiceModel service = project.getServiceMap().get(serviceUniqueKey);
            MethodModel method = null;
            for (MethodModel item : service.getMethodList()) {
                if (item.getName().equals(methodName)) {
                    method = item;
                    break;
                }
            }

            int threadNum = getInt(tfThreadNum, "线程数");
            int totalNum = getInt(tfTotalNum, "总次数");
            int maxResultNum = getConfig(envName).getMaxResultNum();
            Object result;
            if (totalNum == 1 && threadNum == 1) {
                result = DubboUtils.invoke(ipPort, service.getName(), service.getVersion(), service.getGroup(), method, taRequest.getText());
            } else {
                List<Object> resultList = new ArrayList<>();
                CountDownLatch latch = new CountDownLatch(threadNum);
                AtomicInteger counter = new AtomicInteger(totalNum);
                for (int i = 0; i < threadNum; i++) {
                    MethodModel finalMethod = method;
                    new Thread(() -> {

                        while (true) {
                            int currNum = counter.decrementAndGet();
                            if (currNum < 0) {
                                latch.countDown();
                                break;
                            }
                            Object obj = null;
                            try {
                                obj = DubboUtils.invoke(ipPort, service.getName(), service.getVersion(), service.getGroup(), finalMethod, taRequest.getText());
                            } catch (Exception e) {
                                obj = MyUtils.getStackTrace(e);
                            } finally {
                                synchronized (resultList) {
                                    LogUtils.println("请求结果:" + JSON.toJSONString(obj, true));
                                    if (resultList.size() <= maxResultNum) {
                                        resultList.add(obj);
                                    }
                                }
                            }
                        }
                    }).start();
                }
                latch.await();
                result = resultList;
            }
            resultRef.set(JSON.toJSONString(result, SerializerFeature.PrettyFormat, SerializerFeature.WriteDateUseDateFormat, SerializerFeature.MapSortField, SerializerFeature.NotWriteRootClassName));
        } catch (Exception e) {
            String msg = "调用接口失败:\n" + MyUtils.getStackTrace(e);
            resultRef.set(msg);
            LogUtils.println(msg);
        } finally {
            Platform.runLater(() -> {
                if (resultRef.get() != null) {
                    taResponse.replaceText(0, 0, resultRef.get());
                }
                btnGetService.setDisable(false);
                btnSend.setDisable(false);
                btnSend.setText("发送");
            });
        }
    }

    private ConfigModel getConfig(String envName) {
        for (ConfigModel item : configList) {
            if (item.getEnv().equals(envName)) {
                return item;
            }
        }
        return null;
    }

    private void getService() {
        AtomicReference<String> errMsgRef = new AtomicReference<>();
        AtomicReference<Map<String, ProjectModel>> projectMapRef = new AtomicReference<>();
        try {
            Platform.runLater(() -> {
                btnGetService.setText("请求中...");
                btnGetService.setDisable(true);
                btnSend.setDisable(true);
            });
            String envName = cbEnv.getSelectionModel().getSelectedItem();
            ConfigModel config = getConfig(envName);
            LogUtils.println("配置信息:" + JSON.toJSONString(config));
            if (config == null) {
                return;
            }
            Map<String, ProjectModel> projectMap = DubboUtils.getProjectService(config);
            projectMapRef.set(projectMap);
            envProjectMap.put(envName, projectMap);

        } catch (Exception e) {
            errMsgRef.set("获取接口失败:\n" + MyUtils.getStackTrace(e));
            LogUtils.println(errMsgRef.get());

        } finally {
            Platform.runLater(() -> {
                if (errMsgRef.get() != null) {
                    taResponse.clear();
                    taResponse.replaceText(0, 0, errMsgRef.get());
                }
                initProjectComboBox(projectMapRef.get());
                btnGetService.setDisable(false);
                btnSend.setDisable(false);
                btnGetService.setText("获取接口");
            });

        }
    }

    private void initMethodComboBox(List<String> methodList) {
        cbMethod.getItems().clear();
        cbParam.getItems().clear();
        cbParam.getEditor().clear();
        if (methodList == null || methodList.isEmpty()) {
            return;
        }
        for (String method : methodList) {
            cbMethod.getItems().add(method);
        }
        cbMethod.getSelectionModel().selectFirst();
        initParamTpl();
    }

    private void initParamTpl() {
        cbParam.getItems().clear();
        cbParam.getEditor().clear();
        String method = cbMethod.getSelectionModel().getSelectedItem();
        if (StringUtils.isNotBlank(method)) {
            String serviceUniqueKey = cbService.getSelectionModel().getSelectedItem();
            List<String> list = ParamParser.findNameList(serviceUniqueKey, method, paramMap);
            for (String name : list) {
                cbParam.getItems().add(name);
            }
        }
    }

    private void initServiceComboBox(Map<String, ServiceModel> serviceMap) {
        cbService.getItems().clear();
        if (serviceMap == null || serviceMap.isEmpty()) {
            return;
        }
        List<ServiceModel> list = new ArrayList<>(serviceMap.values());
        for (ServiceModel service : list) {
            cbService.getItems().add(service.getUniqueKey());
        }
        cbService.getSelectionModel().selectFirst();
        initMethodComboBox(list.get(0).getMethodNameList());
    }

    private void initProjectComboBox(Map<String, ProjectModel> projectMap) {
        cbProject.getItems().clear();
        cbService.getItems().clear();
        cbIpPort.getItems().clear();
        cbMethod.getItems().clear();
        paramMap.clear();
        if (projectMap == null || projectMap.isEmpty()) {
            return;
        }
        List<ProjectModel> projectList = new ArrayList<>(projectMap.values());
        for (ProjectModel project : projectList) {
            cbProject.getItems().add(project.getAppName());
        }
        cbProject.getSelectionModel().selectFirst();
        paramMap.putAll(ParamParser.parse(cbProject.getSelectionModel().getSelectedItem()));
        initServiceComboBox(projectList.get(0).getServiceMap());
        initIpPortComboBox(projectList.get(0).getIpPortList());

    }

    private void initIpPortComboBox(List<String> ipPortList) {
        cbIpPort.getItems().clear();
        if (ipPortList == null || ipPortList.isEmpty()) {
            return;
        }
        for (String item : ipPortList) {
            cbIpPort.getItems().add(item);
        }
        cbIpPort.getSelectionModel().selectFirst();
    }

    @FXML
    private void btnGetServiceAction(ActionEvent event) {
        new Thread(() -> getService()).start();
    }

    @FXML
    private void onProjectAction(ActionEvent event) {
        paramMap.clear();
        String envName = cbEnv.getSelectionModel().getSelectedItem();
        String appName = cbProject.getSelectionModel().getSelectedItem();
        Map<String, ProjectModel> projectMap = envProjectMap.get(envName);
        if (projectMap == null) {
            return;
        }
        paramMap.putAll(ParamParser.parse(cbProject.getSelectionModel().getSelectedItem()));
        ProjectModel project = projectMap.get(appName);
        if (project != null) {
            initServiceComboBox(project.getServiceMap());
            initIpPortComboBox(project.getIpPortList());
        }
    }

    @FXML
    private void onIpPortAction(ActionEvent event) {

    }

    @FXML
    private void onServiceAction(ActionEvent event) {
        String envName = cbEnv.getSelectionModel().getSelectedItem();
        String appName = cbProject.getSelectionModel().getSelectedItem();
        String serviceUniqueKey = cbService.getSelectionModel().getSelectedItem();
        Map<String, ProjectModel> projectMap = envProjectMap.get(envName);
        if (projectMap == null) {
            return;
        }
        ProjectModel project = projectMap.get(appName);
        if (project == null) {
            return;
        }
        ServiceModel service = project.getServiceMap().get(serviceUniqueKey);
        if (service != null) {
            initMethodComboBox(service.getMethodNameList());
        }
    }

    @FXML
    private void onEnvAction(ActionEvent event) {
        String envName = cbEnv.getSelectionModel().getSelectedItem();
        Map<String, ProjectModel> projectMap = envProjectMap.get(envName);
        initProjectComboBox(projectMap);
    }

    private void initUI() {
        //初始化环境下拉框
        List<String> envList = configList.stream().map(ConfigModel::getEnv).collect(Collectors.toList());
        envList = envList.stream().distinct().collect(Collectors.toList());
        LogUtils.println("环境信息" + envList);
        envList.forEach(key -> cbEnv.getItems().add(key));
        cbEnv.getSelectionModel().selectFirst();
    }

    public void onFormatParam(ActionEvent event) {
        try {
            String str = taRequest.getText();
            str = JSON.toJSONString(JSON.parse(str), SerializerFeature.PrettyFormat, SerializerFeature.WriteDateUseDateFormat, SerializerFeature.MapSortField);
            taRequest.clear();
            taRequest.setText(str);
        } catch (Exception e) {
            taResponse.clear();
            taResponse.replaceText(0, 0, MyUtils.getStackTrace(e));
        }
    }

    public void onSaveParam(ActionEvent event) {
        String paramText = taRequest.getText();
        if (StringUtils.isBlank(paramText)) {
            return;
        }
        String projectName = cbProject.getSelectionModel().getSelectedItem();
        if (StringUtils.isBlank(projectName)) {
            MyUtils.alert("请选择一个项目");
            return;
        }
        String serviceUniqueKey = cbService.getSelectionModel().getSelectedItem();
        if (StringUtils.isBlank(serviceUniqueKey)) {
            MyUtils.alert("请选择一个服务");
            return;
        }
        String methodName = cbMethod.getSelectionModel().getSelectedItem();
        if (StringUtils.isBlank(methodName)) {
            MyUtils.alert("请选择一个方法");
            return;
        }
        String paramTplName = cbParam.getSelectionModel().getSelectedItem();
        if (StringUtils.isBlank(paramTplName)) {
            MyUtils.alert("请选择参数模板,或者输入参数模板名称");
            return;
        }
        String key = ParamParser.buildKey(serviceUniqueKey, methodName, paramTplName);
        LogUtils.println("key=" + key);
        if (!paramMap.containsKey(key)) {
            cbParam.getItems().add(paramTplName);
        }
        paramMap.put(key, paramText);
        ParamParser.save(projectName, paramMap);
    }

    public void onMethodAction(ActionEvent event) {
        initParamTpl();
    }

    public void onParamAction(ActionEvent event) {
        String paramTplName = cbParam.getSelectionModel().getSelectedItem();
        if (!cbParam.getItems().contains(paramTplName)) {
            //修改参数模板下拉框内容不触发事件
            return;
        }
        String method = cbMethod.getSelectionModel().getSelectedItem();
        String serviceUniqueKey = cbService.getSelectionModel().getSelectedItem();
        String key = ParamParser.buildKey(serviceUniqueKey, method, paramTplName);
        String value = paramMap.get(key);
        if (value == null) {
            value = "";
        } else {
            try {
                value = JSON.toJSONString(JSON.parse(value), SerializerFeature.PrettyFormat, SerializerFeature.WriteDateUseDateFormat, SerializerFeature.MapSortField);
            } catch (Exception e) {
            }
        }
        taRequest.setText(value);
    }
}
